{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3b5b077d",
   "metadata": {},
   "source": [
    "# RunnablePassthrough\n",
    "\n",
    "`RunnablePassthrough` 는 데이터를 전달하는 역할을 합니다. 이 클래스는 `invoke()` 메서드를 통해 **입력된 데이터를 그대로 반환** 합니다.\n",
    "\n",
    "이는 데이터를 변경하지 않고 파이프라인의 다음 단계로 전달하는 데 사용될 수 있습니다.\n",
    "\n",
    "`RunnablePassthrough` 는 다음과 같은 시나리오에서 유용할 수 있습니다.\n",
    "\n",
    "- 데이터를 변환하거나 수정할 필요가 없는 경우\n",
    "- 파이프라인의 특정 단계를 건너뛰어야 하는 경우\n",
    "- 디버깅 또는 테스트 목적으로 데이터 흐름을 모니터링해야 하는 경우\n",
    "\n",
    "이 클래스는 `Runnable` 인터페이스를 구현하므로, 다른 `Runnable` 객체와 함께 파이프라인에서 사용될 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "be73a3b5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# API 키를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API 키 정보 로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "47e9d66d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH13-LCEL-Advanced\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dd223232",
   "metadata": {},
   "source": [
    "## 데이터 전달하기\n",
    "\n",
    "`RunnablePassthrough` 는 입력을 변경하지 않고 그대로 전달하거나 추가 키를 더하여 전달할 수 있습니다.\n",
    "\n",
    "일반적으로 `RunnableParallel` 과 함께 사용되어 데이터를 맵의 새로운 키에 할당하는 데 활용됩니다.\n",
    "\n",
    "`RunnablePassthrough()` 를 단독으로 호출하면 단순히 입력을 받아 그대로 전달합니다.\n",
    "\n",
    "assign과 함께 호출된 RunnablePassthrough(`RunnablePassthrough.assign(...)`)는 입력을 받아 assign 함수에 전달된 추가 인자를 더합니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "992bca8a",
   "metadata": {},
   "source": [
    "- `RunnableParallel` 클래스를 사용하여 **병렬로 실행 가능한 작업을 정의** 합니다.\n",
    "- `passed` 속성에는 `RunnablePassthrough` 인스턴스를 할당하여 입력을 그대로 반환하도록 설정합니다.\n",
    "- `extra` 속성에는 `RunnablePassthrough.assign()` 메서드를 사용하여 입력의 \"num\" 값에 3을 곱한 결과를 \"mult\" 키에 할당하는 작업을 정의합니다.\n",
    "- `modified` 속성에는 람다 함수를 사용하여 입력의 \"num\" 값에 1을 더하는 작업을 정의합니다.\n",
    "- `runnable.invoke()` 메서드를 호출하여 `{\"num\": 1}` 입력으로 병렬 작업을 실행합니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "94e3a547",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnableParallel, RunnablePassthrough\n",
    "\n",
    "runnable = RunnableParallel(\n",
    "    # 전달된 입력을 그대로 반환하는 Runnable을 설정합니다.\n",
    "    passed=RunnablePassthrough(),\n",
    "    # 입력의 \"num\" 값에 3을 곱한 결과를 반환하는 Runnable을 설정합니다.\n",
    "    extra=RunnablePassthrough.assign(mult=lambda x: x[\"num\"] * 3),\n",
    "    # 입력의 \"num\" 값에 1을 더한 결과를 반환하는 Runnable을 설정합니다.\n",
    "    modified=lambda x: x[\"num\"] + 1,\n",
    ")\n",
    "\n",
    "# {\"num\": 1}을 입력으로 Runnable을 실행합니다.\n",
    "runnable.invoke({\"num\": 1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f2aa62a7",
   "metadata": {},
   "outputs": [],
   "source": [
    "r = RunnablePassthrough.assign(mult=lambda x: x[\"num\"] * 3)\n",
    "r.invoke({\"num\": 1})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3552773b",
   "metadata": {},
   "source": [
    "위의 예시에서 `passed` 키는 `RunnablePassthrough()`와 함께 호출되었으며, 이는 단순히 `{'num': 1}`을 전달합니다.\n",
    "\n",
    "두 번째 줄에서는 숫자 값에 3을 곱하는 람다 함수와 함께 `RunnablePastshrough.assign`을 사용했습니다. 이 경우, `extra`는 원래 값에 `mult` 키가 추가된 `{'num': 1, 'mult': 3}`로 설정되었습니다.\n",
    "\n",
    "마지막으로, `modified` 키를 사용하여 맵에 세 번째 키를 설정했는데, 이는 람다 함수를 사용하여 num에 1을 더한 단일 값을 설정하였으며, 그 결과 `modified` 키의 값은 `2`가 되었습니다.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0846106",
   "metadata": {},
   "source": [
    "## 검색기 예제\n",
    "\n",
    "아래 예제에서는 `RunnablePassthrough` 사용하는 사용 사례를 살펴볼 수 있습니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ea470fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.vectorstores import FAISS\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import RunnablePassthrough\n",
    "from langchain_openai import ChatOpenAI, OpenAIEmbeddings\n",
    "\n",
    "# 텍스트로부터 FAISS 벡터 저장소를 생성합니다.\n",
    "vectorstore = FAISS.from_texts(\n",
    "    [\n",
    "        \"테디는 랭체인 주식회사에서 근무를 하였습니다.\",\n",
    "        \"셜리는 테디와 같은 회사에서 근무하였습니다.\",\n",
    "        \"테디의 직업은 개발자입니다.\",\n",
    "        \"셜리의 직업은 디자이너입니다.\",\n",
    "    ],\n",
    "    embedding=OpenAIEmbeddings(),\n",
    ")\n",
    "# 벡터 저장소를 검색기로 사용합니다.\n",
    "retriever = vectorstore.as_retriever()\n",
    "# 템플릿을 정의합니다.\n",
    "template = \"\"\"Answer the question based only on the following context:\n",
    "{context}\n",
    "\n",
    "Question: {question}\n",
    "\"\"\"\n",
    "# 템플릿으로부터 채팅 프롬프트를 생성합니다.\n",
    "prompt = ChatPromptTemplate.from_template(template)\n",
    "\n",
    "# ChatOpenAI 모델을 초기화합니다.\n",
    "model = ChatOpenAI(model_name=\"gpt-4o-mini\")\n",
    "\n",
    "\n",
    "# 문서를 포맷팅하는 함수\n",
    "def format_docs(docs):\n",
    "    return \"\\n\".join([doc.page_content for doc in docs])\n",
    "\n",
    "\n",
    "# 검색 체인을 구성합니다.\n",
    "retrieval_chain = (\n",
    "    {\"context\": retriever | format_docs, \"question\": RunnablePassthrough()}\n",
    "    | prompt\n",
    "    | model\n",
    "    | StrOutputParser()\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b4955e13",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 검색 체인을 실행하여 질문에 대한 답변을 얻습니다.\n",
    "retrieval_chain.invoke(\"테디의 직업은 무엇입니까?\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "771ffeb2",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 검색 체인을 실행하여 질문에 대한 답변을 얻습니다.\n",
    "retrieval_chain.invoke(\"셜리의 직업은 무엇입니까?\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "py-test",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
